﻿using System;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;
using System.Runtime.Serialization;
using System.Text;
using System.Web;


namespace Sharp.Infrastructure.Search
{
    public partial class QuerySupport<TEntity>
    {

        #region 过滤条件
        /// <summary>
        /// 数值型
        /// </summary>
        private static readonly Type[] NumberType = { typeof(int), typeof(decimal), typeof(float), typeof(double) };

        public ParameterExpression Parameter { get; set; }

        /// <summary>
        /// 创建查询条件
        /// </summary>
        /// <param name="pf"></param>
        /// <returns></returns>
        public Expression<Func<TEntity, bool>> TobuildFilter(PagingFeature pf)
        {
            if (pf.RequestString.IsNullOrEmpty())
            {
                return x => true;
            }
            List<SearchCondition> searchs = new List<SearchCondition>();

            var nameValue = HttpUtility.ParseQueryString(pf.RequestString);

            #region 自动创建
            for (int i = 0; i < nameValue.Keys.Count; i++)
            {
                string key = nameValue.Keys[i];
                string value = nameValue[i];
                if (value.IsNullOrEmpty() || value == "null" || hulie(key))
                {
                    continue;
                }

                string fieldName = nameValue.Keys[i].Substring(0, 1).ToUpper() + nameValue.Keys[i].Substring(1);

                var property = typeof(TEntity).GetProperty(fieldName);

                if (property == null)
                {
                    continue;
                }
                if (property.PropertyType.IsAssignableFrom(typeof(DateTime)))
                {
                    #region 起始框搜索


                    searchs.Add(new SearchCondition(property.Name, key, SearchType.Section,
                        nameValue[fieldName + "_from"], nameValue[fieldName + "_to"]));

                    #endregion
                }
                if (property.PropertyType.IsAssignableFrom(typeof(Guid)))
                {
                    searchs.Add(new SearchCondition(property.Name, key, SearchType.In, nameValue[fieldName]));
                }
                else if (property.PropertyType.IsValueType && !nameValue[fieldName].Contains(",")) //值类型直接等于
                {
                    searchs.Add(new SearchCondition(property.Name, key, SearchType.Equal, nameValue[fieldName]));
                }
                else if (NumberType.Any(x => property.PropertyType.IsAssignableFrom(x))
                   && nameValue[fieldName].Contains(","))
                {
                    searchs.Add(new SearchCondition(property.Name, key, SearchType.In, nameValue[fieldName]));
                }
                else if (!property.PropertyType.IsValueType && property.PropertyType != typeof(string))//导航属性查询
                {
                    searchs.Add(new SearchCondition(property.Name, key, SearchType.Equal, nameValue[fieldName]));
                }
                else if (property.PropertyType == typeof(string))
                {
                    searchs.Add(new SearchCondition(property.Name, key, SearchType.Like, nameValue[fieldName]));
                }
            }

            #endregion

            #region 追加和覆盖条件
            pf.Filters.ToList().ForEach(item =>
            {
                if (searchs.All(x => x.For != item.For))
                {
                    searchs.Add(item);
                }
                //TODO:覆盖
            });

            #endregion

            return ToFilter(searchs);
        }
        /// <summary>
        /// 生成筛选表达
        /// </summary>
        public Expression<Func<TEntity, bool>> ToFilter(List<SearchCondition> searchs)
        {
            if (searchs == null)
            {
                return x => true;
            }
             
            if (Parameter == null)
            {
                Parameter= Expression.Parameter(typeof(TEntity), "m");
            }

            Expression expression = Expression.Constant(true);


            foreach (var search in searchs)
            {
                var property = typeof(TEntity).GetProperty(search.For);
                var fieldName = property.Name;
                if (search.How == SearchType.Section)
                {
                    #region 起始框搜索

                    BinaryExpression beginThan = null, endThan = null;
                    if (search.Values[0] != null)
                    {
                        //暂时只考虑日期型
                        DateTime dt;
                        if (DateTime.TryParse(search.Values[0], out dt))
                        {
                            ConstantExpression begin = Expression.Constant(dt, property.PropertyType);
                            beginThan = Expression.GreaterThanOrEqual(Expression.Property(Parameter, fieldName), begin);

                        }
                    }
                    if (search.Values[1] != null)
                    {
                        DateTime dt;
                        if (DateTime.TryParse(search.Values[1], out dt))
                        {
                            ConstantExpression end = Expression.Constant(dt, property.PropertyType);
                            endThan = Expression.LessThanOrEqual(Expression.Property(Parameter, fieldName), end);
                        }
                    }
                    if (beginThan != null)
                        expression = Expression.And(beginThan, expression);
                    if (endThan != null)
                        expression = Expression.And(endThan, expression);

                    #endregion
                }
                if (search.How == SearchType.In)
                {
                    //如果没有数据
                    if (search.Value.Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries).Length == 0)
                    {
                        //expression = Expression.And(expression, Expression.Constant(false));
                        continue;
                    }


                    #region 外键搜索，兼容单选多选
                    if (property.PropertyType.IsAssignableFrom(typeof(Guid)))
                    {
                        var values = new List<Guid>();

                        foreach (var s in search.Value.Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries))
                        {
                            values.Add(Guid.Parse(s));
                        }

                        expression = Expression.And(GetConditionExpression(Parameter, values, fieldName), expression);
                    }
                    else
                    {
                        var values = new List<int>();

                        foreach (var s in search.Value.Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries))
                        {
                            values.Add(int.Parse(s));
                        }

                        expression = Expression.And(GetConditionExpression(Parameter, values, fieldName), expression);
                    }

                    #endregion
                }
                else if (search.How == SearchType.Equal)
                {
                    Expression constant;
                    if (!property.PropertyType.GenericTypeArguments.Any())
                    {
                        constant = Expression.Constant(ConvertHelper.ChangeType(search.Value, property.PropertyType));
                    }
                    else
                    {
                        constant = Expression.Constant(ConvertHelper.ChangeType(search.Value, property.PropertyType.GenericTypeArguments.First()));
                    }


                    Expression right = Expression.Equal(Expression.Property(Parameter, fieldName),
                       Expression.Convert(constant, property.PropertyType));

                    expression = Expression.And(right, expression);

                }
                else if (!property.PropertyType.IsValueType && property.PropertyType != typeof(string))
                {
                    Expression exroperty = null;
                    var fields = search.Key.Split(new[] { '.', '_' }, StringSplitOptions.RemoveEmptyEntries);


                    for (var index = 1; index < fields.Length; index++)
                    {
                        var pro = typeof(TEntity).GetProperty(fields[index]);
                        if (exroperty == null)
                        {
                            exroperty = Expression.Property(Parameter, pro.PropertyType.GetMethod("get_" + pro.Name));
                        }
                        else
                        {
                            var parentpro = typeof(TEntity).GetProperty(fields[index - 1]);

                            exroperty = Expression.Property(exroperty, parentpro.PropertyType.GetMethod("get_" + fields[index]));
                        }
                    }

                    if (exroperty == null)
                    {
                        throw new ArgumentNullException("一定有问题");
                    }
                    Expression right = Expression.Equal(
                        exroperty,
                        Expression.Constant(search.Value, typeof(string)),
                        false,
                        typeof(string).GetMethod("op_Equality"));


                    expression = Expression.And(right, expression);
                }
                else if (property.PropertyType == typeof(string) || search.How == SearchType.Like)
                {
                    #region 输入框搜索

                    Expression right = Expression.Call(Expression.Property(Parameter, fieldName),
                        typeof(string).GetMethod("Contains"),
                        Expression.Constant(search.Value));

                    expression = Expression.And(right, expression);

                    #endregion
                }
            }


            var filter = Expression.Lambda<Func<TEntity, bool>>(expression, Parameter);

            return filter;
        }


        #endregion



        #region 分隔表单字段Key
        /// <summary>
        /// 分隔表单字段Key
        /// </summary>
        /// <param name="key"></param>
        /// <param name="tableName"></param>
        /// <param name="fieldName"></param>
        /// <returns></returns>
        public static Boolean SplitFieldName(string key,
            out string tableName, out string fieldName)
        {
            if (string.IsNullOrEmpty(key))
            {
                tableName = "";
                fieldName = "";
                return false;
            }
            var arr = key.Split(new char[] { '_', '.' });
            tableName = arr[0];
            fieldName = arr[1];
            return true;
        }
        #endregion

        #region 表达式

        /// <summary>
        /// 表达式
        /// </summary>
        /// <param name="left"></param>
        /// <param name="options"></param>
        /// <param name="fieldName"></param>
        /// <returns></returns>
        private static Expression GetConditionExpression(ParameterExpression left, IEnumerable<string> options, string fieldName)
        {
            Expression expression = Expression.Constant(false);
            foreach (var optionName in options)
            {
                Expression right = Expression.Call
                       (
                          Expression.Property(left, typeof(TEntity).GetProperty(fieldName)),
                          typeof(string).GetMethod("Equals", new[] { typeof(string) }),
                           Expression.Constant(optionName, typeof(string))
                       );

                expression = Expression.Or(right, expression);
            }
            return expression;
        }
        private static Expression GetConditionExpression(ParameterExpression left, IEnumerable<int> options, string fieldName)
        {
            Expression expression = Expression.Constant(false);
            foreach (var optionName in options)
            {
                Expression constant = Expression.Constant(ConvertHelper.ChangeType(optionName, typeof(int)));

                Expression right = Expression.Equal(Expression.Property(left, fieldName),
                   Expression.Convert(constant, typeof(int)));

                expression = Expression.Or(right, expression);
            }
            return expression;
        }

        private static Expression GetConditionExpression(ParameterExpression left, IEnumerable<Guid> options, string fieldName)
        {
            Expression expression = Expression.Constant(false);

            foreach (var optionName in options)
            {
                Expression constant = Expression.Constant(optionName);
                MethodInfo m;
                var t = typeof(TEntity).GetProperty(fieldName).PropertyType;
                if (t == typeof(Guid?))
                {
                    m = typeof(Guid?).GetMethod("op_Equality");
                }
                else
                {
                    m = typeof(Guid).GetMethod("Equal");
                }

                Expression right = Expression.Equal(Expression.Property(left, typeof(TEntity).GetProperty(fieldName)),
                    Expression.Convert(Expression.Constant(optionName), t), false, m);

                expression = Expression.Or(right, expression);
            }
            return expression;
        }



        private Expression Join(Expression left, string fieldName)
        {
            var property = typeof(TEntity).GetProperty(fieldName);

            Expression expression = Expression.Property(left,
                property.PropertyType.GetMethod("get_" + property.Name));

            return expression;
        }

        #endregion

        private bool hulie(string key)
        {
            if (new[] { "draw", "length", "start" }.Contains(key))
            {
                return true;
            }
            if (key.StartsWith("columns[") || key.StartsWith("search["))
            {
                return true;
            }
            return false;
        }
    }
    [DataContract]
    public class SearchCondition
    {
        public SearchCondition(string @for, string key, SearchType how, params string[] values)
        {
            For = @for;
            Key = key;
            How = how;

            Values = values;
        }
        /// <summary>
        /// 搜索哪个字段
        /// </summary>
        [DataMember]
        public string For { get; set; }
        /// <summary>
        /// request 的key
        /// </summary>
        [DataMember]
        public string Key { get; set; }

        [DataMember]
        public SearchType How { get; set; }

        public string Value
        {
            get { return Values[0]; }
        }

        [DataMember]    
        public string[] Values { get; set; }
    }
}
